#include "utils.h"

HANDLE hHookAccount=NULL;
static PROTOACCOUNT **regAccounts=NULL;
static int numRegAccounts;

//This function refresh account list. 
//It called when SMS plugin loaded and upon change in the account list.
int RefreshAccountList( WPARAM eventCode, LPARAM lParam )
{
	int count,i;
	PROTOACCOUNT **tmpAccounts;
	char tmpService[256];
	numRegAccounts = 0;
	ProtoEnumAccounts( &count, &tmpAccounts );
	lstrcpyA(tmpService,"\0");
	mir_free(regAccounts);
	for (i=0; i<count; i++)
	{
		lstrcpyA(tmpService,tmpAccounts[i]->szModuleName);
		lstrcatA(tmpService,MS_SENDSMS);	
		if (ServiceExists(tmpService))
		{
			numRegAccounts++;
			if(numRegAccounts == 1)
				regAccounts=(PROTOACCOUNT**)mir_alloc(sizeof(PROTOACCOUNT*)*1);
			else
				regAccounts=(PROTOACCOUNT**)mir_realloc(regAccounts,sizeof(PROTOACCOUNT*)*numRegAccounts);
			regAccounts[numRegAccounts-1]=tmpAccounts[i];
		}
		lstrcpyA(tmpService,"\0");
	}

	UpdateAllSendSMSWindowsAccountLists();
	UpdateSMSOptionsAccounts();
	//DEBUG
	//for(i=0;i<numRegAccounts;i++)
	//{
	//	MessageBoxA(NULL,regAccounts[i].szModuleName,"SMS Plugin DEBUG",MB_OK);
	//}
	//END DEBUG
	return 0;
}
//This function free the global account list. 
//This function should be called before list refresh or when SMS plugin unloaded.
void FreeAccountList()
{
	mir_free(regAccounts);
}
//This funciton retrieves the amount of accounts in miranda that supports sms sending.
int GetNumAccountList()
{
	return numRegAccounts;
}
//This function check if the module is in the account list that supports sms sending in miranda.
//1 if yes, 0 otherwise.
int IsInAccountList(char* szModule)
{
	int i;
	for(i=0;i<numRegAccounts;i++)
		if(!lstrcmpA(regAccounts[i]->szModuleName,szModule));
			return 1;
	return 0;
}
//This function gets a pointer to account list that supports SMS sending in miranda
//and the amount of this accounts.
void GetAccountList(int* iNumAcc,TCHAR*** tAccList)
{
	int i;
	*iNumAcc=numRegAccounts;
	*tAccList=(TCHAR**)mir_alloc(sizeof(TCHAR*)*numRegAccounts);
	for(i=0;i<numRegAccounts;i++)
	{
		*(*tAccList+i)=(TCHAR*)mir_alloc(sizeof(TCHAR)*(lstrlen(CHAR2TCHAR(regAccounts[i]->szModuleName))+1));
		lstrcpy(*(*tAccList+i),(TCHAR*)CHAR2TCHAR(regAccounts[i]->szModuleName));
	}
}
//This function converts CHAR types into WCHAR.
LPWSTR Char2WChar (const char* pCstring)
{
    LPWSTR pszOut = NULL;
    if (pCstring != NULL)
    {
        int nInputStrLen = (int)strlen (pCstring);
        // Double NULL Termination
        int nOutputStrLen = MultiByteToWideChar(CP_ACP, 0, pCstring, nInputStrLen, NULL, 0) + 2;
        pszOut = mir_alloc(sizeof(WCHAR)*nOutputStrLen);
        if (pszOut)
        {
            memset (pszOut, 0x00, sizeof (WCHAR)*nOutputStrLen);
            MultiByteToWideChar (CP_ACP, 0, pCstring, nInputStrLen, pszOut, nInputStrLen);
        }
    }
    return pszOut;
}
//This function converts WCHAR types into CHAR.
LPSTR WChar2Char(LPCWSTR s)
{
	int cw;
	if (s==NULL) return NULL;
	cw=lstrlenW(s);
	if (cw==0) {
		CHAR *psz=(CHAR*)mir_alloc(sizeof(CHAR)*1);
		*psz='\0';
		return psz;
	}
	{
		int cc=WideCharToMultiByte(CP_ACP,0,s,cw,NULL,0,NULL,NULL);
		if (cc==0) return NULL;
		{
			CHAR *psz=(CHAR*)mir_alloc(sizeof(CHAR)*(cc+1));
			cc=WideCharToMultiByte(CP_ACP,0,s,cw,psz,cc,NULL,NULL);
			if (cc==0) {
				mir_free(psz);
				return NULL;
			}
			psz[cc]='\0';
			return psz;
		}
	}
}
//This function get a string cellular number and return the HANDLE of the contact that has this
//number in the miranda phonebook (and marked as an SMS able) at the User Details.
//If no one has this number function returns NULL.
HANDLE CellularToHandle(const TCHAR *cNumber)
{
	HANDLE hContactTmp=NULL;
	int i;
	char idstr[256];
	HANDLE hContact=NULL;
	DBVARIANT dbv;
	hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDFIRST,0,0);
	while(hContact!=NULL) 
	{
		char *szProto;
		szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
		if(!DBGetContactSettingTString(hContact,szProto,"Cellular",&dbv)) 
		{
			if(lstrlen(dbv.ptszVal)>4 && !lstrcmp(dbv.ptszVal+lstrlen(dbv.ptszVal)-4,_T(" SMS"))) 
			{
				dbv.ptszVal[lstrlen(dbv.ptszVal)-4]=_T('\0');
				if(!lstrcmp(strCellular(dbv.ptszVal),strCellular(cNumber)))
				{
					hContactTmp = hContact;
					return hContactTmp;				
				}
			}
			DBFreeVariant(&dbv);
		}
		for(i=0;;i++) 
		{
			wsprintfA(idstr,"MyPhone%d",i);
			if(DBGetContactSettingTString(hContact,"UserInfo",idstr,&dbv))
				break;
			wsprintfA(idstr,Translate("Custom %d"),i+1);
			if(lstrlen(dbv.ptszVal)>4 && !lstrcmp(dbv.ptszVal+lstrlen(dbv.ptszVal)-4,_T(" SMS"))) 
			{
				dbv.ptszVal[lstrlen(dbv.ptszVal)-4]=_T('\0');
				if(!lstrcmp(strCellular(dbv.ptszVal),strCellular(cNumber)))
				{
					hContactTmp = hContact;
					return hContactTmp;
				}
			}
			DBFreeVariant(&dbv);
		}
		hContact=(HANDLE)CallService(MS_DB_CONTACT_FINDNEXT,(WPARAM)hContact,0);
	}
	return (HANDLE)NULL;
}

//Decode XML coded string. The function translate special xml code into standard characters.
TCHAR* DecodeXML(const TCHAR* cXml)
{
	int i;
	TCHAR *cDecodedXML;
	cDecodedXML=(TCHAR*)mir_alloc(1*sizeof(TCHAR));
	*cDecodedXML=_T('\0');
	for(i=0;i<lstrlen(cXml);i++)
	{
		TCHAR cTmp[2];
		cTmp[0]=*(cXml+i);
		cTmp[1]=_T('\0');
		if(!_tcsncmp(cXml+i,_T("&apos;"),6))
		{
			cDecodedXML=(TCHAR*)mir_realloc(cDecodedXML,sizeof(TCHAR) * (1 + lstrlen(cDecodedXML)));
			lstrcat(cDecodedXML,_T("\'"));
			i=i+5;
		}
		else if(!_tcsncmp(cXml+i,_T("&quot;"),6))
		{
			cDecodedXML=(TCHAR*)mir_realloc(cDecodedXML,sizeof(TCHAR) * (1 + lstrlen(cDecodedXML)));
			lstrcat(cDecodedXML,_T("\""));
			i=i+5;
		}
		else if(!_tcsncmp(cXml+i,_T("&amp;"),5))
		{
			cDecodedXML=(TCHAR*)mir_realloc(cDecodedXML,sizeof(TCHAR) * (1 + lstrlen(cDecodedXML)));
			lstrcat(cDecodedXML,_T("&"));
			i=i+4;
		}
		else if(!_tcsncmp(cXml+i,_T("&lt;"),4))
		{
			cDecodedXML=(TCHAR*)mir_realloc(cDecodedXML,sizeof(TCHAR) * (1 + lstrlen(cDecodedXML)));
			lstrcat(cDecodedXML,_T("<"));
			i=i+3;
		}
		else if(!_tcsncmp(cXml+i,_T("&gt;"),4))
		{
			cDecodedXML=(TCHAR*)mir_realloc(cDecodedXML,sizeof(TCHAR) * (1 + lstrlen(cDecodedXML)));
			lstrcat(cDecodedXML,_T(">"));
			i=i+3;
		}
		else
		{
			cDecodedXML=(TCHAR*)mir_realloc(cDecodedXML,sizeof(TCHAR) * (2 + lstrlen(cDecodedXML)));
			lstrcat(cDecodedXML,cTmp);
		}
	}
	cDecodedXML=(TCHAR*)mir_realloc(cDecodedXML,sizeof(TCHAR) * (1 + lstrlen(cDecodedXML)));
	lstrcat(cDecodedXML,_T("\0"));
	return cDecodedXML;
}

//This function get a XML string and return the asked tag.
char *GetXMLField(const char *xml,const char *tag1,...)
{
	va_list va;
	int thisLevel,i,start=-1;
	const char *findTag;

	va_start(va,tag1);
	thisLevel=0;
	findTag=tag1;
	for(i=0;xml[i];i++) {
		if(xml[i]=='<') {
			char tag[64],*tagend=strchr(xml+i,'>');
			if(tagend==NULL) return NULL;
			lstrcpynA(tag,xml+i+1,min((int)sizeof(tag),tagend-xml-i));
			if(tag[0]=='/') {
				if(--thisLevel<0) {
					char *ret;
					if(start==-1) return NULL;
					ret=(char*)mir_alloc(i-start+1);
					lstrcpynA(ret,xml+start,i-start+1);
					return ret;
				}
			}
			else {
				if(++thisLevel==1 && !lstrcmpiA(tag,findTag)) {
					findTag=va_arg(va,const char*);
					if(findTag==NULL) start=tagend-xml+1;
					thisLevel=0;
				}
			}
			i=tagend-xml;
		}
	}
	va_end(va);
	return NULL;
}

//Encode XML coded string. The function translate special saved xml characters into special characters.
TCHAR* EncodeXML(const TCHAR* cXml)
{
	int i;
	TCHAR *cEncodedXML;
	cEncodedXML=(TCHAR*)mir_alloc(1*sizeof(TCHAR));
	*cEncodedXML=_T('\0');
	for(i=0;i<lstrlen(cXml);i++)
	{
		TCHAR cTmp[2];
		cTmp[0]=*(cXml+i);
		cTmp[1]=_T('\0');
		if(!_tcsncmp(cXml+i,_T("\'"),1))
		{
			cEncodedXML=(TCHAR*)mir_realloc(cEncodedXML,sizeof(TCHAR) * (6 + lstrlen(cEncodedXML)));
			lstrcat(cEncodedXML,_T("&apos;"));
		}
		else if(!_tcsncmp(cXml+i,_T("\""),1))
		{
			cEncodedXML=(TCHAR*)mir_realloc(cEncodedXML,sizeof(TCHAR) * (6 + lstrlen(cEncodedXML)));
			lstrcat(cEncodedXML,_T("&quot;"));
		}
		else if(!_tcsncmp(cXml+i,_T("&"),1))
		{
			cEncodedXML=(TCHAR*)mir_realloc(cEncodedXML,sizeof(TCHAR) * (5 + lstrlen(cEncodedXML)));
			lstrcat(cEncodedXML,_T("&amp;"));
		}
		else if(!_tcsncmp(cXml+i,_T("<"),1))
		{
			cEncodedXML=(TCHAR*)mir_realloc(cEncodedXML,sizeof(TCHAR) * (4 + lstrlen(cEncodedXML)));
			lstrcat(cEncodedXML,_T("&lt;"));
		}
		else if(!_tcsncmp(cXml+i,_T(">"),1))
		{
			cEncodedXML=(TCHAR*)mir_realloc(cEncodedXML,sizeof(TCHAR) * (4 + lstrlen(cEncodedXML)));
			lstrcat(cEncodedXML,_T("&gt;"));
		}
		else
		{
			cEncodedXML=(TCHAR*)mir_realloc(cEncodedXML,sizeof(TCHAR) * (2 + lstrlen(cEncodedXML)));
			lstrcat(cEncodedXML,cTmp);
		}
	}
	cEncodedXML=(TCHAR*)mir_realloc(cEncodedXML,sizeof(TCHAR) * (1 + lstrlen(cEncodedXML)));
	lstrcat(cEncodedXML,_T("\0"));
	return cEncodedXML;
}

//This function gets a Cellular string number and clean it from symbools.
TCHAR *strCellular(const TCHAR* str)
{
	int i,j=0;
	TCHAR *cReturn;
	cReturn=(TCHAR *)mir_alloc(50 * sizeof(TCHAR));
	for(i=0;i <= (signed)(lstrlen(str) - 1);i++)
	{
		if((*(str + i) >= _T('0')) && (*(str + i) <= _T('9')))
		{
			cReturn[j]=*(str + i);
			j++;
		}
	}
	cReturn[j]=_T('\0');
	return cReturn;
}

extern HINSTANCE hInst;
//This function return plugin HINSTANCE.
HINSTANCE GetPluginhInst()
{
	return hInst;
}
struct UTF8_INTERFACE utfi;
struct MM_INTERFACE mmi;

void InitUtils()
{
	mir_getUTFI( &utfi );
	mir_getMMI( &mmi );
	hHookAccount=HookEvent(ME_PROTO_ACCLISTCHANGED,RefreshAccountList);
	RefreshAccountList((WPARAM)NULL,(LPARAM)NULL);	
}
void UninitUtils()
{
	FreeAccountList();
	UnhookEvent(hHookAccount);
}